﻿using System.Collections.Generic;
using System;
using System.Text;

namespace Ex03.GarageLogic
{
    /// <summary>
    /// This class is an abstract class for a Vehicle
    /// </summary>
    public abstract class Vehicle
    {
        private const string k_PlateNumberKey = "Plate Number";
        private const string k_VehicleModelKey = "Vehicle Model";
        private const string k_EnergyLeftInPercentKey = "Energy Left In Percent";
        public const string k_ToStirngPairDelimiter = " : ";
        public static readonly string sr_ToStirngEndOfPairDelimiter = System.Environment.NewLine; // stylecop demands capital letter after small but we need the sr_ prefix for static readonly
        private static readonly Dictionary<string, string> sr_VehicleForm = null;
        
        private readonly string r_PlateNumber;
        private readonly List<Wheel> m_Wheels;
        private string m_Model = null;
        private float m_EnergyLeftInPercent = 0.0f;

        /// <summary>
        /// A static method to check the validity of plate numbers
        /// </summary>
        /// <param name="i_PlateNumber">
        ///     The plate number to check
        ///     <remarks>
        ///         will return true if the plate number is not null and
        ///         its length is greater then zero
        ///     </remarks>
        /// </param>
        /// <returns></returns>
        public static bool IsValidPlateNumber(string i_PlateNumber)
        {
            return i_PlateNumber != null && i_PlateNumber.Length != 0;
        }

        /// <summary>
        /// This is a static constructor that will initialize the Vehicle 
        /// form 
        /// </summary>
        static Vehicle()
        {
            sr_VehicleForm = new Dictionary<string, string>();
            sr_VehicleForm.Add(k_VehicleModelKey, null);
        }

        /// <summary>
        /// A constructor that will initialize read-only members
        /// </summary>
        /// <param name="i_PlateNumber">
        ///     The plate number
        ///     <remarks>
        ///         Must not be null or of length 0, in such a case
        ///         will throw FormatException
        ///     </remarks>
        /// </param>
        /// <param name="i_NumberOfWheels">
        ///     The number of wheels
        ///     <remarks>
        ///         Must be grater then zero, if not will throw ArgumentException
        ///     </remarks>
        /// </param>
        /// <param name="i_WheelMaxAirPressure">
        ///     The wheel max air pressure
        ///     <see cref="Wheel"/>
        /// </param>
        protected Vehicle(string i_PlateNumber, int i_NumberOfWheels, float i_WheelMaxAirPressure)
        {
            int wheelIndex;
            if (!IsValidPlateNumber(i_PlateNumber))
            {
                throw new FormatException("Plate number of vehicle is invalid");
            }

            if (i_NumberOfWheels <= 0)
            {
                throw new ArgumentException("Number of wheels in vehicle is invalid (below zero)");
            }

            r_PlateNumber = i_PlateNumber;
            m_Wheels = new List<Wheel>(i_NumberOfWheels);
            for (int i = 0; i < i_NumberOfWheels; ++i)
            {
                wheelIndex = i + 1;
                m_Wheels.Add(new Wheel(i_WheelMaxAirPressure, wheelIndex));
            }
        }

        /// A property to get the Vehicle form
        /// <remarks>will return a new form</remarks>
        /// </summary>
        protected static Dictionary<string, string> VehicleForm
        {
            get { return new Dictionary<string, string>(sr_VehicleForm); }
        }

        /// <summary>
        /// A property to get the plate number
        /// </summary>
        public string PlateNumber
        {
            get { return r_PlateNumber; }
        }

        /// <summary>
        /// A property to get the wheels
        /// </summary>
        public List<Wheel> Wheels
        {
            get { return m_Wheels; }
        }

        /// <summary>
        /// An abstract property to get the energy left in percent
        /// </summary>
        public abstract float EnergyLeftInPercent { get; }

        /// <summary>
        /// An abstract method to get the form of the vehicle 
        /// </summary>
        public abstract Dictionary<string, string> GetForm();
        
        /// <summary>
        /// This is the main initialization function of this object
        /// This function will receive a form with all the data needed for the initialization
        /// </summary>
        /// <param name="i_CreationForm">
        /// A key value Dictionary where key is the name of the member 
        /// to initialize and value is the new value
        /// </param>
        public virtual void Initialize(Dictionary<string, string> i_CreationForm)
        {
            bool isGetValueSucceded;

            isGetValueSucceded = i_CreationForm.TryGetValue(k_VehicleModelKey, out m_Model);
            if (!isGetValueSucceded)
            {
                throw new FormatException("Vehicle Model is invalid");
            }

            intializeWheels(i_CreationForm);
        }

        private void intializeWheels(Dictionary<string, string> i_CreationForm)
        {
            foreach (Wheel wheel in m_Wheels)
            {
                wheel.InitializeByForm(i_CreationForm);
            }
        }
        
        /// <summary>
        /// Return a string representation object members as pair 
        /// members name and variable value
        /// </summary>
        /// <returns>string "varName + Vehicle.k_ToStirngPairDelimiter + varValue + Vehicle.sr_ToStirngEndOfPairDelimiter"</returns>
        public override string ToString()
        {
            StringBuilder stringBuilderResult = new StringBuilder();

            stringBuilderResult.Append(plateNumberToString());
            stringBuilderResult.Append(VehicleModeToString());
            stringBuilderResult.Append(EnergyLeftInPercentToString());
            foreach (Wheel currentWheel in m_Wheels)
            {
                stringBuilderResult.Append(currentWheel.ToString());
            }

            return stringBuilderResult.ToString();
        }

        private string plateNumberToString()
        {
            StringBuilder stringBuilderResult = new StringBuilder();

            stringBuilderResult.Append(k_PlateNumberKey);
            stringBuilderResult.Append(k_ToStirngPairDelimiter);
            stringBuilderResult.Append(r_PlateNumber);
            stringBuilderResult.Append(sr_ToStirngEndOfPairDelimiter);

            return stringBuilderResult.ToString();
        }

        private string VehicleModeToString()
        {
            StringBuilder stringBuilderResult = new StringBuilder();

            stringBuilderResult.Append(k_VehicleModelKey);
            stringBuilderResult.Append(k_ToStirngPairDelimiter);
            stringBuilderResult.Append(m_Model);
            stringBuilderResult.Append(sr_ToStirngEndOfPairDelimiter);

            return stringBuilderResult.ToString();
        }

        private string EnergyLeftInPercentToString()
        {
            StringBuilder stringBuilderResult = new StringBuilder();

            stringBuilderResult.Append(k_EnergyLeftInPercentKey);
            stringBuilderResult.Append(k_ToStirngPairDelimiter);
            stringBuilderResult.Append(m_EnergyLeftInPercent);
            stringBuilderResult.Append(sr_ToStirngEndOfPairDelimiter);

            return stringBuilderResult.ToString();
        }
    }
}
